#include "ChatService.hpp"
#include <muduo/base/Logging.h>
#include <iostream>
#include <string>

using namespace std;
using namespace muduo::net;
using namespace muduo;
using namespace placeholders;
ChatService::ChatService() {
    this->handlers.insert(
        {EnMsHandler::LOGIN,
         std::bind(&ChatService::LoginHandler, this, _1, _2, _3)});
    this->handlers.insert(
        {EnMsHandler::REGISTER,
         std::bind(&ChatService::RegisterHandler, this, _1, _2, _3)});
    this->handlers.insert(
        {EnMsHandler::SEND_MSG,
         std::bind(&ChatService::OnChatHandler, this, _1, _2, _3)});
    this->handlers.insert(
        {EnMsHandler::ADD_FRIEND,
         std::bind(&ChatService::AddFriendHanlder, this, _1, _2, _3)});
    this->handlers.insert(
        {EnMsHandler::CREATE_GROUP,
         std::bind(&ChatService::CreateGroup, this, _1, _2, _3)});
    this->handlers.insert({EnMsHandler::ADD_GROUP,
                          std::bind(&ChatService::AddGroup, this, _1, _2, _3)});
    handlers.insert({EnMsHandler::GROUP_CHAT,
                     std::bind(&ChatService::ChatGroup, this, _1, _2, _3)});
    handlers.insert({EnMsHandler::LOGINOUT, std::bind(&ChatService::Loginout, this, _1, _2, _3)});

    // 当该服务器收到redis订阅的消息后，调用SubscribeHandler
    if (_redis.connect()) {
        _redis.init_notify_handler(std::bind(&ChatService::SubscribeHandler, this, _1, _2));
    }
}

ChatService::~ChatService() {}
void ChatService::LoginHandler(const TcpConnectionPtr& conn,
                               json& js,
                               muduo::Timestamp) {
    LOG_INFO << "LoginHandler";
    // 根据id查找用户, 验证密码
    const int id = js["id"].get<int>();
    const string password = js["password"].get<string>();
    auto user = userModel.Query(id);
    if (user.getId() == -1 || user.getPassword() != password) {
        // 用户不存在
        json res;
        res["errno"] = 1;
        res["errmsg"] = "用户不存在或密码错误";
        conn->send(res.dump());
        return;
    }
    // {"msgId": 0, "id": 13, "password": "123456"}
    // 如果已在线，提醒已经在线
    const string state = user.getState();
    if (state == "online") {
        json res;
        res["errno"] = 1;
        res["errmsg"] = "用户已在线";
        conn->send(res.dump());
        return;
    }
    {
        lock_guard<mutex> lock(mtx);
        conns.insert({id, conn});
    }
    const string name = user.getName();
    // 成功登录后，更新状态
    userModel.UpdateState(user);
    // 在redis中订阅该用户
    _redis.subscribe(id);
    // 登录成功，返回用户信息
    json res;
    res["msgId"] = EnMsHandler::LOGIN_ASK;
    res["id"] = user.getId();
    res["name"] = user.getName();
    res["errno"] = 0;
    // 查询是否有离线消息
    auto offlineMsg = offlineMsgModel.Query(id);
    if (!offlineMsg.empty()) {
        res["offlineMsg"] = offlineMsg;
        offlineMsgModel.Delete(id);
    }
    // 查询好友列表
    auto firends = friendModel.query(id);
    if (!firends.empty()) {
        vector<string> friendList;
        for (auto it = firends.begin(); it != firends.end(); it++) {
            char buff[1024]{0};
            sprintf(buff, "{\"id\":%d,\"name\":\"%s\",\"state\":\"%s\"}",
                    it->getId(), it->getName().c_str(), it->getState().c_str());
            cout << buff << endl;
            friendList.push_back(buff);
        }
        res["friends"] = friendList;
    }
    // 查询所在群组的成员信息
    auto groups = groupModel.QueryGroupInfo(id);
    if (!groups.empty()) {
        vector<string> groupList;
        const int len = groups.size();
        groupList.reserve(len);
        for (auto info : groups) {
            json group;
            group["id"] = info.GetId();
            group["name"] = info.GetName();
            group["desc"] = info.GetDesc();
            vector<string> users;
            for (auto user : info.GetUsers()) {
                json u;
                u["id"] = user.getId();
                u["name"] = user.getName();
                u["state"] = user.getState();
                u["role"] = user.getRole();
                users.push_back(u.dump());
            }
            group["users"] = users;
            groupList.push_back(group.dump());
        }
        res["groups"] = groupList;
    }
    conn->send(res.dump());
}
void ChatService::RegisterHandler(const TcpConnectionPtr& conn,
                                  json& js,
                                  muduo::Timestamp) {
    LOG_INFO << "RegisterHandler";
    cout << js.dump() << endl;
    const string name = js["name"];
    const string password = js["password"];
    User user(-1, name, password);
    bool state = userModel.Insert(user);
    if (state) {
        // 注册成功，返回用户的id
        json res;
        res["msgId"] = EnMsHandler::REGISTER_ASK;
        res["id"] = user.getId();
        res["errno"] = 0;
        conn->send(res.dump());
    } else {
        json res;
        res["errno"] = 1;
        res["errmsg"] = "The user name already exists!";
        conn->send(res.dump());
    }
}
ChatService* ChatService::Instance() {
    static ChatService service;
    return &service;
}
MsHandler ChatService::GetHandler(EnMsHandler msgId) {
    auto res = handlers.find(msgId);
    if (res == handlers.end()) {
        return [](const muduo::net::TcpConnectionPtr& conn, json& js,
                  muduo::Timestamp) -> void {
            LOG_INFO << "not find your handler!";
        };
    }
    return res->second;
}
// 处理用户异常退出的情况
void ChatService::ClinetCloseException(
    const muduo::net::TcpConnectionPtr& conn) {
    User user;
    int id = -1;
    mtx.lock();
    for (auto it = conns.begin(); it != conns.end(); it++) {
        if (it->second == conn) {
            id = it->first;
            conns.erase(it);
            break;
        }
    }
    mtx.unlock();
    if (id == -1)
        return;
    user = userModel.Query(id);
    userModel.UpdateState(user, "offline");
}
void ChatService::OnChatHandler(const TcpConnectionPtr& conn,
                                json& js,
                                muduo::Timestamp) {
    // {"msgId": 0, "id": 22, "password": "123456"}
    // {"msgId": 0, "id": 13, "password": "123456"}
    // {"msgId": 2, "id": 22, "name": "Zhang San", "toId": 22, "msg": "hello
    // world"}
    LOG_INFO << "OnChatHandler";
    const int toId = js["toId"].get<int>();
    {
        lock_guard<mutex> lock(mtx);
        auto it = conns.find(toId);
        // 找到对方的连接，发送消息
        if (it != conns.end()) {
            it->second->send(js.dump());
            return;
        }
        auto User = userModel.Query(toId);
        if (User.getState() == "online") {
            // 发送到redis中
            _redis.publish(toId, js.dump());
            return;
        }
    }
    // 如果不在线, 保存离线消息
    offlineMsgModel.Insert(toId, js.dump());
}

void ChatService::Reset() {
    userModel.Reset();
}

void ChatService::AddFriendHanlder(const muduo::net::TcpConnectionPtr& conn,
                                   json& js,
                                   muduo::Timestamp) {
    LOG_INFO << "AddFriendHanlder";
    const int id = js["userId"].get<int>();
    const int friendId = js["friendId"].get<int>();
    friendModel.insert(id, friendId);
    // {"msgId": 0, "id": 13, "password": "123456"}
    // {"msgId": 3, "userId": 2, "friendId": 22}
}
void ChatService::CreateGroup(const muduo::net::TcpConnectionPtr& conn,
                              json& js,
                              muduo::Timestamp) {
    const int id = js["userid"].get<int>();
    const string groupName = js["groupname"].get<string>();
    const string groupDesc = js["groupdesc"].get<string>();
    Group group;
    group.SetName(groupName);
    group.SetDesc(groupDesc);
    // 创建群组完成后，让给用户加入到群聊，并设置改用户role为创建者用户
    bool c = groupModel.CreateGroup(group);
    if (c) {
        groupModel.AddGroup(id, group.GetId(), "creator");
    }
    // {"msgId": 4, "userid": 2, "groupname": "group1", "groupdesc": "group1
    // desc"}
}

void ChatService::AddGroup(const muduo::net::TcpConnectionPtr& conn,
                           json& js,
                           muduo::Timestamp) {
    const int id = js["userId"].get<int>();
    const int groupId = js["groupId"].get<int>();
    groupModel.AddGroup(id, groupId, "normal");
}
void ChatService::ChatGroup(const muduo::net::TcpConnectionPtr& conn,
                            json& js,
                            muduo::Timestamp) {
    const int id = js["userId"].get<int>();
    const int groupId = js["groupId"].get<int>();
    auto members = groupModel.QueryGroupUsers(id, groupId);
    for (int userId : members) {
        auto it = conns.find(userId);
        if (it != conns.end()) {
            cout << "chat group send to " << userId << endl;
            it->second->send(js.dump());
        } else {
            // 如果找不到，有可能登录在其它服务器上
            auto User = userModel.Query(userId);
            if (User.getState() == "online") {
                // 发送到redis中
                _redis.publish(userId, js.dump());
            } else {
                // 离线消息保存
                offlineMsgModel.Insert(userId, js.dump());
            }
        }
    }
}

void ChatService::Loginout(const muduo::net::TcpConnectionPtr& conn, json& js, muduo::Timestamp) {
    const int id = js["userId"].get<int>();
    User user = userModel.Query(id);
    userModel.UpdateState(user, "offline");
    // 从conns中删除
    lock_guard<mutex> lock(mtx);
    conns.erase(id);
    // 并且取消redis的订阅
    if (_redis.connect()) {
        _redis.unsubscribe(id);
    }
}

void ChatService::SubscribeHandler(int id, string msg) {
    auto it = conns.find(id);
    if (it != conns.end()) {
        it->second->send(msg);
    }
}
